/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package zombiedefense.map;

import zombiedefense.util.Vector2D;
import java.awt.geom.Point2D;
import java.util.Iterator;
import zombiedefense.framework.PlayerManager;
import zombiedefense.framework.ZombieManager;
import zombiedefense.sprites.Sprite;
import zombiedefense.sprites.Zombie;
import static zombiedefense.util.Vector2D.scratch;
import static zombiedefense.util.Vector2D.scratch2;
/**
 *
 * @author &{user}
 */
public class Raycaster {
    
    private TileMap map;
    private PlayerManager player;
    private ZombieManager zombies;
    
    //constants
    private static final double checkInterval = .3;
    private static double jumpX,jumpY,x,y;  //raycasting variables
    private static byte collision;
    private static boolean w,p,z;
    
    public static final byte DETECT_WALLS_MASK = 1;
    public static final byte DETECT_PLAYER_MASK = 2;
    public static final byte DETECT_ZOMBIES_MASK = 4;
    
    public Raycaster(TileMap map,PlayerManager player,ZombieManager zombies){
	this.map = map;
	this.player = player;
	this.zombies = zombies;
    }
    
    public byte raycast(Sprite source,Point2D.Double collisionPoint,double startx,double starty,double rads,byte mode){
        //calculate distances to move based on angle
        return raycast(source,collisionPoint,startx,starty,mode,Math.cos(rads) * checkInterval,Math.sin(rads) * checkInterval);
    }
    
    //core of the algorithm
    private byte raycast(Sprite source,Point2D.Double collisionPoint,double startx,double starty,byte mode,double jumpX,double jumpY){
        //figure out which conditions to check
	w = (mode & DETECT_WALLS_MASK) > 0;
	p = (mode & DETECT_PLAYER_MASK) > 0;
	z = (mode & DETECT_ZOMBIES_MASK) > 0;
	
	x = startx;
	y = starty;
	
	while(true){
	    collision = isCollision(source,x,y);
	    
	    if(collision != 0){
		if(collisionPoint != null)
		    collisionPoint.setLocation(x,y);
		
		return collision;
	    }
	    
	    x += jumpX;
	    y += jumpY;
	}
	
    }
    
    /**
     * This method implements the simple raycasting algorithm. The mode
     * parameter should be composed of masks and represents which types of
     * objects the raycaster should detect. The value returned will be equal
     * to zero or one of the masks, indicating what type of collision occurred.
     * If the collided object is a sprite, its rayHit method is called.
     * @param collisionPoint
     * @param startx
     * @param starty
     * @param degrees 
     */
    public byte raycast(Sprite source,Point2D.Double collisionPoint,double startx,double starty,double endx,double endy,byte mode){
	
	//start vector
        scratch.setTo(startx,starty);
        scratch2.setTo(endx,endy);
        
        scratch2.subtract(scratch); //vector between start and end
        scratch2.normalize();
        scratch2.scale(checkInterval);
        
	//calculate distances to jump on both axes
	jumpX = scratch2.x;
	jumpY = scratch2.y;
        
        //call the helper method
        return raycast(source,collisionPoint,startx,starty,mode,jumpX,jumpY);
    }
    
    
    
    private byte isCollision(Sprite source,double x,double y){
	
	if(w){
	    if(map.isSolid(x, y))return DETECT_WALLS_MASK;
	}
	
	if(p){
	    if(player.getPlayer().isInsideBounds(x, y)){
		player.getPlayer().rayHit(source);
		return DETECT_PLAYER_MASK;
	    }	
	}
	
	if(z){
	    Iterator i = zombies.getZombies();
	    while(i.hasNext()){
		Zombie z = (Zombie)i.next();
		if(z.isInsideBounds(x, y)){
		    z.rayHit(source);
		    return DETECT_ZOMBIES_MASK;
		}
	    }
	}
	
	return 0;
	
    }
}
